package com.gzxx.security.filter;

import com.gzxx.constant.RedisCacheConstant;
import com.gzxx.exception.ServiceException;
import com.gzxx.security.CurrentUser;
import com.gzxx.security.TokenService;
import com.gzxx.util.GsonUtil;
import com.gzxx.util.JwtOperator;
import com.redisdatastorage.cache.RedisCache;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author MAX
 * @date 2020/7/24 11:15 上午
 * Jwt过滤器
 */

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final JwtOperator jwtOperator;
    private final RedisCache redisCache;
    private final TokenService tokenService;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        //从header中获取token
        String authorization = request.getHeader("Authorization");
        if (StringUtils.hasLength(authorization) && authorization.startsWith("Bearer")) {
            String token = authorization.substring(7);
            try {
                Claims claims = jwtOperator.parseToken(token);
                if (ObjectUtils.isEmpty(claims)) {
                    throw new RuntimeException("token解析异常");
                }
                String uuid = (String) claims.get("identifier");
                if (!redisCache.hasKey(RedisCacheConstant.CURRENT_LOGIN_USER_KEY + uuid)) {
                    throw new RuntimeException("token已过期");
                } else {
                    // 刷新redis token
                    tokenService.refreshToken(request);
                }
                String json = redisCache.get(RedisCacheConstant.CURRENT_LOGIN_USER_KEY + uuid);
                CurrentUser currentUser = GsonUtil.fromJson(json, CurrentUser.class);
                if (currentUser != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(currentUser, null, currentUser.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            } catch (ExpiredJwtException e) {
                throw new AccountExpiredException("登陆状态已过期", e);
            } catch (MalformedJwtException e) {
                throw new ServiceException("token构造不正确", e);
            } catch (Exception e) {
                throw new RuntimeException("token解析异常", e);
            }
        }
        chain.doFilter(request, response);
    }
}
